/**
 * Copyright (c) 2015-2019, Golly Hu 胡高 (3722711@qq.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */
package cn.hg.jboot.core.directive;

import java.util.Collection;

import com.jfinal.plugin.activerecord.SqlPara;
import com.jfinal.plugin.activerecord.sql.JbsaDirectiveBase;
import com.jfinal.template.Env;
import com.jfinal.template.TemplateException;
import com.jfinal.template.expr.ast.Const;
import com.jfinal.template.expr.ast.Expr;
import com.jfinal.template.expr.ast.ExprList;
import com.jfinal.template.io.Writer;
import com.jfinal.template.stat.ParseException;
import com.jfinal.template.stat.Scope;

import io.jboot.web.directive.annotation.JFinalDirective;

/**
 * SQL模块中，IN参数指令
 * 
 * <pre>
 * SQL模板：WHERE t1.id #inParas(list)
 * 参数：list = [1, 2, 3]
 * 解析结果：WHERE t1.id IN (?, ?, ?)
 * </pre>
 * 
 * @author 胡高
 */
@JFinalDirective(InParasDirective.DIRECTIVE_NAME)
public class InParasDirective extends JbsaDirectiveBase {

    /**
     * 指令名称
     */
    public static final String DIRECTIVE_NAME = "inParas";

    private int index = -1;

    private int addPara(SqlPara sqlPara, Object val, Writer writer, int flag) {
        this.write(writer, flag > 0 ? ", ?" : "?");
        sqlPara.addPara(val);
        return flag + 1;
    }

    @Override
    public void onRender(Env env, Scope scope, Writer writer) {
        SqlPara sqlPara = (SqlPara)scope.get(SQL_PARA_KEY);

        if (sqlPara == null) {
            throw new TemplateException("#" + DIRECTIVE_NAME + " directive invoked by getSqlPara(...) method only",
                this.location);
        }

        Object[] paraArray = this.exprList.evalExprList(scope);
        int flag = 0; // IN参数个数

        this.write(writer, "IN (");
        for (Object para : paraArray) {

            if (para == null) { // 优先处理参数为Null的情况
                flag = this.addPara(sqlPara, para, writer, flag);
            } else if (para instanceof Collection<?>) { // 处理参数为Collection的情况
                Collection<?> paraCollection = (Collection<?>)para;

                for (Object obj : paraCollection) {
                    flag = this.addPara(sqlPara, obj, writer, flag);
                }

            } else if (para.getClass().isArray()) { // 处理参数为Array的情况
                Object[] objArray = (Object[])para;

                for (Object object : objArray) {
                    flag = this.addPara(sqlPara, object, writer, flag);
                }

            } else { // 处理参数为一般类型的情况
                flag = this.addPara(sqlPara, para, writer, flag);
            }

        }

        this.write(writer, ")");
    }

    @Override
    public void setExprList(ExprList exprList) {

        if (exprList.length() == 0) {
            throw new ParseException("The parameter of #" + DIRECTIVE_NAME + " directive can not be blank",
                this.location);
        }

        if (exprList.length() == 1) {
            Expr expr = exprList.getExpr(0);

            if (expr instanceof Const && ((Const)expr).isInt()) {
                this.index = ((Const)expr).getInt();

                if (this.index < 0) {
                    throw new ParseException("The index of inparas array must greater than -1", this.location);
                }

            }

        }

        this.exprList = exprList;
    }

}
